Skip to content

feat(secrets): add Scaleway Secret Manager backend#152

Merged
opzkit-auto-merge[bot] merged 1 commit into
mainfrom
feat/scaleway-secret-backend
May 7, 2026
Merged

feat(secrets): add Scaleway Secret Manager backend#152
opzkit-auto-merge[bot] merged 1 commit into
mainfrom
feat/scaleway-secret-backend

Conversation

@peter-svensson

Copy link
Copy Markdown
Member

Summary

Adds a fourth backend implementation alongside aws, kubernetes, and infisical (introduced in #135). Stores generated database credentials in Scaleway Secret Manager scoped to a (Region, Project) pair, authenticated via a Scaleway IAM API key read from a Kubernetes Secret.

Auth-Secret shape (access_key + secret_key) intentionally mirrors the External Secrets Operator's Scaleway provider so the same IAM key can back both stores when a cluster runs ESO + DBUO.

CRD shape

spec:
  secretBackend:
    scaleway:
      region: fr-par           # required, enum {fr-par, nl-ams, pl-waw}
      projectID: <uuid>        # required
      description: ...         # optional
      tags:                    # optional, k=v map
        Environment: production
      authSecretRef:
        name: scaleway-dbuo-creds

A full sample lives at config/samples/database_v1alpha1_database_scaleway.yaml.

Implementation notes

  • Versioning: each Update creates a new Scaleway SecretVersion. The 1-based monotonic Revision is returned as the version string in Status.SecretVersion.
  • Tags: Scaleway stores tags as a flat []string. The map is flattened to key=value entries (sorted for stability), and SyncTags replaces the full set wholesale via UpdateSecret.
  • Name sanitisation: Scaleway accepts [A-Za-z0-9-_.] in secret names. The default rds/<engine>/<db> name (slashes) is sanitised to rds_<engine>_<db> — same approach as the Infisical backend.
  • Restore-on-create: matches the AWS / Kubernetes / Infisical contract — if a Secret already exists at this name, Create adds a new version + refreshes tags/description rather than failing.

Required Scaleway IAM permissions

The supplied API key must hold:

Scope Permission set
Org SecretManagerReadOnly
Project SecretManagerSecretAccess

Org-scope ReadOnly covers the ListSecrets (lookup-by-name) call we issue on every reconcile; Scaleway's IAM evaluator rejects it at Project scope. Project-scope SecretAccess covers the decrypt path used by Get / read.

Tests

internal/secrets/scaleway_test.go adds 12 cases via an in-memory fakeScalewayClient implementing the ScalewaySMClient interface:

  • Create_NewSecret + Create_RestoreOnExisting
  • Get_RoundTrip + Get_NotFound
  • Update_NewVersion + Update_NotFound
  • Delete (incl. delete-on-missing as no-op) + SyncTags + SyncTags_NotFound
  • NameSanitisation
  • NewBackend_Validation (table-driven: empty region, invalid region, empty projectID, empty auth)
  • StoredPayloadIsDatabaseSecretJSON (ensures the wire format matches the AWS / K8s / Infisical backends so consumers like ExternalSecrets templates work uniformly)

New dep footprint

github.com/scaleway/scaleway-sdk-go (~5 MB vendored). Used only for api/secret/v1beta1 + scw core; no transitive dependencies beyond what the SDK ships.

Smoke-tested against

Drafted as a regression-test vehicle. Ready for review when you've eyeballed the design.

Adds a fourth backend implementation alongside aws / kubernetes /
infisical. Scaleway-region scoping + Project-UUID + IAM API key auth
(read from a Kubernetes Secret holding `access_key` + `secret_key` —
same shape as ESO's Scaleway provider expects).

CRD shape:

  spec:
    secretBackend:
      scaleway:
        region: fr-par           # required, enum {fr-par, nl-ams, pl-waw}
        projectID: <uuid>        # required
        description: ...         # optional
        tags: { ... }            # optional, k=v map
        authSecretRef:
          name: scaleway-dbuo-creds

Wire-format: each Update creates a new Scaleway SecretVersion (1-based
monotonic Revision returned as the version string). Tags are flattened
to "key=value" entries and replaced wholesale on SyncTags. Secret
names containing slashes (e.g. the AWS-style `rds/postgres/<db>`
default) are sanitised to underscores; Scaleway's allowed character
set is `[A-Za-z0-9-_.]`.

Permissions required on the IAM Application:

  - SecretManagerReadOnly       at Org scope (covers list-by-name)
  - SecretManagerSecretAccess   at Project scope (covers decrypt)

Tests cover: create-new, restore-on-existing, get round-trip,
not-found errors, update-creates-new-version, delete idempotence,
SyncTags replace-set, name sanitisation, validation in
NewScalewayBackend, and stored-payload JSON shape.
@peter-svensson peter-svensson marked this pull request as ready for review May 7, 2026 17:09
@peter-svensson peter-svensson requested a review from argoyle as a code owner May 7, 2026 17:09
@peter-svensson

Copy link
Copy Markdown
Member Author

Smoke-tested end-to-end on Scaleway staging (Kapsule + managed RDB PostgreSQL 16, fr-par, in-cluster operator pulled pr-152 image):

{
  "actualSecretName": "dbuo_scw_smoketest_creds",
  "actualUsername": "dbuo_scw_smoketest",
  "connectionInfo": {
    "database": "dbuo_scw_smoketest",
    "engine": "postgres",
    "host": "172.16.0.3",
    "port": 5432,
    "username": "dbuo_scw_smoketest"
  },
  "databaseCreated": true,
  "userCreated": true,
  "secretCreated": true,
  "secretFormatVersion": "v2",
  "secretLocator": "scaleway://fr-par/<project-uuid>/dbuo_scw_smoketest_creds",
  "secretVersion": "1",
  "phase": "Ready"
}

Reconcile log walks through the expected path: Granting privileges → Privileges granted successfully → Migrating secret to new format → Creating new secret in backend → Secret created successfully → Syncing secret tags → Reconciliation successful. Subsequent reconciles short-circuit via "Resources already exist and spec unchanged, skipping reconciliation".

Required IAM perms confirmed (only):

  • Org-scope SecretManagerReadOnly (covers list-by-name)
  • Project-scope SecretManagerFullAccess (covers create/update/access/delete on the SM secret + its versions)

Reads back through the existing ESO Scaleway provider against the same secret without changes — interoperability with external-secrets.io/v1 ClusterSecretStores intact. Ready for review.

@opzkit-auto-merge opzkit-auto-merge Bot enabled auto-merge (squash) May 7, 2026 17:37

@argoyle argoyle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚖️ 🛣️

@opzkit-auto-merge opzkit-auto-merge Bot merged commit 8b5abde into main May 7, 2026
7 of 8 checks passed
@opzkit-auto-merge opzkit-auto-merge Bot deleted the feat/scaleway-secret-backend branch May 7, 2026 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants